﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using VectorHelper;

namespace _2D碰撞
{
    public class Helper
    {
        public static Ball BallMove(Ball ball)
        {
            ball.X += ball.velocity.X;
            ball.Y += ball.velocity.Y;
            return ball;
        }

        public static PointF GetTangentPoint(PointF ptCenter, PointF ptOutside, double r)
        {

            PointF E = new PointF(), F = new PointF(), G = new PointF(), H = new PointF();
            //1. 坐标平移到圆心ptCenter处,求园外点的新坐标E 
            E.X = ptOutside.X - ptCenter.X;
            E.Y = ptOutside.Y - ptCenter.Y; //平移变换到E 

            //2. 求园与OE的交点坐标F, 相当于E的缩放变换 
            double t = r / Math.Sqrt(E.X * E.X + E.Y * E.Y); //得到缩放比例 
            F.X = (float)(E.X * t); F.Y = (float)(E.Y * t); //缩放变换到F 

            //3. 将E旋转变换角度a到切点G，其中cos(a)=r/OF=t, 所以a=arccos(t); 
            double a = Math.Acos(t); //得到旋转角度 
            G.X = (float)(F.X * Math.Cos(a) - F.Y * Math.Sin(a));
            G.Y = (float)(F.X * Math.Sin(a) + F.Y * Math.Cos(a)); //旋转变换到G 

            //4. 将G平移到原来的坐标下得到新坐标H 
            H.X = G.X + ptCenter.X;
            H.Y = G.Y + ptCenter.Y; //平移变换到H 

            //5. 返回H 

            return H;
        }

        /// <summary>
        /// 判断是否相撞
        /// </summary>
        /// <param name="Pa"></param>
        /// <param name="Pb"></param>
        /// <returns></returns>
        public static IsCollision CollisionTest(List<PointF[]> lines, Ball ball)
        {
            IsCollision result = new IsCollision() { isCollision = false, Plst = new List<PointF[]>() };
            foreach (PointF[] item in lines)
            {
                float s, t; // 线段方程的两个参数 
                PointF Pa = item[0]; PointF Pb = item[1];

                // 障碍线段
                float x_v1 = Pb.X - Pa.X;
                float y_v1 = Pb.Y - Pa.Y;
                // 物体速度向量
                float x_v2 = ball.velocity.X;
                float y_v2 = ball.velocity.Y;
                // 障碍向量初始点
                float x_v1b = Pa.X;
                float y_v1b = Pa.Y;

                //求切点
                //PointF p = new PointF();
                //p = GetTangentPoint(new PointF(ball.X, ball.Y), Pa, ball.R);


                // 物体位置
                float x_v2b = ball.X;
                float y_v2b = ball.Y;

                // 计算d,d1和d2
                //      | x_v1           -x_v2          |   
                //d =   | y_v1           -y_v2          | 
                //      | x_v2b-x_v1b    -x_v2          |
                //d1 =  | y_v2b-y_v1b    -y_v2          |
                //      | x_v1           x_v2b-x_v1b    |
                //d2 =  | y_v1           y_v2b-y_v1b    | 

                var d = (x_v1 * (-y_v2)) - ((-x_v2) * y_v1);
                var d1 = ((x_v2b - x_v1b) * (-y_v2)) - ((-x_v2) * (y_v2b - y_v1b));
                var d2 = (x_v1 * (y_v2b - y_v1b)) - ((x_v2b - x_v1b) * y_v1);

                // 判断d是否为零
                if (Math.Abs(d) < 0.001f) // 如果等于零做近似处理,abs()用于求绝对值
                    d = 0.001f;
                // 计算参量s,t
                s = d1 / d;
                t = d2 / d;

                // 判断是否发生碰撞
                // 如果发生了就返回true
                if (0.0f <= s && 1.0f >= s && 0.0f <= t && 1.0f >= t)
                {
                    result.isCollision = true;
                    result.Plst.Add(new PointF[] { Pa, Pb });
                    return result;
                }
            }
            return result;
        }

        /// <summary>
        /// 计算向量反弹
        /// </summary>
        /// <param name="Pa"></param>
        /// <param name="Pb"></param>
        /// <returns></returns>
        public static Ball Rebound(Ball ball, List<PointF[]> Plst)
        {
            Vector f = new Vector(0, 0);
            foreach (PointF[] item in Plst)
            {
                PointF Pa = item[0]; PointF Pb = item[1];


                //向量ab
                Vector P_ab = Func.SetVector(Pa.X, Pa.Y, Pb.X, Pb.Y);
                //向量ab逆时针90°
                Vector N = new Vector(P_ab.Y, -P_ab.X);
                //T向量的模
                double N_mod = Func.Mod(N);
                //T的单位向量
                Vector N_o = new Vector((float)(N.X / N_mod), (float)(N.Y / N_mod));
                //小球的速度向量的同模反向量
                Vector V_f = new Vector(-ball.velocity.X, -ball.velocity.Y);
                //V_f向量在T向量上的投影长 d = V_f * N_o 
                double d = (V_f.X * N_o.X + V_f.Y * N_o.Y);
                //投影向量n为 T_o*d
                Vector n = new Vector((float)(N_o.X * d), (float)(N_o.Y * d));
                //F = (m+n)*2-m 
                Vector F = new Vector((float)((ball.velocity.X + n.X) * 2 - ball.velocity.X), (float)((ball.velocity.Y + n.Y) * 2 - ball.velocity.Y));
                f.X += F.X;
                f.Y += F.Y;
            }
            ball.velocity = f;
            return ball;
        }

    }


    public class IsCollision
    {
        public bool isCollision { get; set; }
        public List<PointF[]> Plst { get; set; }
    }

}
